#ifndef HOST_PROXY_H
#define HOST_PROXY_H

#include "ark_runtime/jsvm.h"
#include "common.h"

namespace rnjsvm {
class JSVMRuntime;

class HostProxy {
protected:
    enum class ProxyType : uint8_t { T_NONE = 0, T_HOST_OBJECT, T_HOST_FUNCTION, T_NATIVE_STATE };

    HostProxy(ProxyType type) : type(type) {}

public:
    static HostProxy *GetHostProxyFromJSVM(JSVM_Env env, JSVM_Value value);

    bool HasNativeState() const { return type == ProxyType::T_NATIVE_STATE; }

    bool IsHostObject() const { return type == ProxyType::T_HOST_OBJECT; }

    bool IsHostFunction() const { return type == ProxyType::T_HOST_FUNCTION; }

    static void Finalizer(JSVM_Env env, void *finalizeData, void *finalizeHint) {
        delete reinterpret_cast<HostProxy *>(finalizeData);
    }

private:
    ProxyType type;
};

class HostObjectProxy : public HostProxy {
public:
    HostObjectProxy(JSVMRuntime &rt, JSVM_Env env, std::shared_ptr<HostObject> ho);

    static JSVM_Value Enumerator(JSVM_Env env, JSVM_Value thisArg, JSVM_Value namedPropertyData);

    static JSVM_Value Getter(JSVM_Env env, JSVM_Value name, JSVM_Value thisArg, JSVM_Value namedPropertyData);

    static JSVM_Value Setter(JSVM_Env env, JSVM_Value name, JSVM_Value property, JSVM_Value thisArg,
							  JSVM_Value namedPropertyData);

	static JSVM_PropertyHandlerConfigurationStruct *GetConfigure() { return &propertyCfg; }

	std::shared_ptr<HostObject> GetHostObject() const { return hostObject; }

private:
	static JSVM_PropertyHandlerConfigurationStruct propertyCfg;
    std::shared_ptr<HostObject> hostObject;
	JSVMRuntime &rt;
};

class ObjectNativeState: public HostProxy {
public:
	ObjectNativeState(JSVMRuntime &rt, std::shared_ptr<NativeState> state)
		: HostProxy(ProxyType::T_NATIVE_STATE), nativeState(state), rt(rt) {}

	std::shared_ptr<NativeState> GetNativeState() const { return nativeState; }

private:
    std::shared_ptr<NativeState> nativeState;
    JSVMRuntime &rt;
};

class HostFunctionProxy: public HostProxy {
public:
	HostFunctionProxy(JSVMRuntime &rt, HostFunctionType hostFunction)
		: HostProxy(ProxyType::T_HOST_FUNCTION), hostFunction(hostFunction), rt(rt) {
		param.callback = FunctionCallback;
		param.data = static_cast<void *>(this);
	}

    static JSVM_Value FunctionCallback(JSVM_Env env, JSVM_CallbackInfo info);

    JSVM_CallbackStruct *GetCallback() { return &param; }

    HostFunctionType &GetHostFunction() { return hostFunction; }



private:
    JSVM_CallbackStruct param;
    HostFunctionType hostFunction;
    JSVMRuntime &rt;
};

}
#endif
